#include <iostream>
#include <allegro.h>
#include <ctime>

#include "..\commons\sprites\spriteMap.h"

#include "MessageIdentifiers.h"
#include "RakNetworkFactory.h"
#include "RakPeerInterface.h"
#include "RakNetTypes.h"
#include "BitStream.h"

using namespace std;
using namespace RakNet;

struct FPSLimiter
{
	FPSLimiter()
	{
		lastTime = clock();
	}
	
	void limitFps( int FPS )
	{
		unsigned int deltaTime = 1000 / FPS;
		unsigned int currentDelta = clock() - lastTime;
		if ( currentDelta < deltaTime )
		{
			rest(deltaTime - currentDelta - 1);
		}
		lastTime = clock();
	}
	
	unsigned int lastTime;
};

enum {
	USER_PACKET = ID_USER_PACKET_ENUM
};

/*void serializeString( Bitstream* bitstream, const std::string& str )
{
	bitstream->Write(
}*/

class Ball
{
	public:
	
	Ball( float x_, float y_ ) : x(x_), y(y_)
	{
		radius = 15;
		xspd = rand()/( (float)RAND_MAX ) * 5;
		yspd = rand()/( (float)RAND_MAX ) * 5;
	}
	
	void update()
	{
		xspd *= 0.98;
		yspd *= 0.98;
		x += xspd;
		y += yspd;
	}
	
	void render( BITMAP* where )
	{
		circlefill(where, x, y, radius, makecol(255,255,255) );
	}
	
	void pack( BitStream& bitstream )
	{
		bitstream.Write(x);
		bitstream.Write(y);
		bitstream.Write(xspd);
		bitstream.Write(yspd);
	}
	
	void unpack( BitStream& bitstream )
	{
		bitstream.Read(x);
		bitstream.Read(y);
		bitstream.Read(xspd);
		bitstream.Read(yspd);
	}
	
	float radius;
	float x;
	float y;
	float xspd;
	float yspd;
};

class Game{
	public:
	Game()
	{
		for ( int i = 0; i < 20; ++i )
		{
			balls.push_back( Ball( 100 + i * 40, 2 * 100 ) );
		}
	}
	
	void update()
	{
		for ( int i = 0; i < balls.size(); ++i )
		{
			balls[i].update();
		}
	}
	
	void render( BITMAP* where )
	{
		for ( int i = 0; i < balls.size(); ++i )
		{
			balls[i].render(where);
		}
	}
	
	void pack( BitStream& bitstream )
	{
		bitstream.Write( balls.size() );
		for ( int i = 0; i < balls.size(); ++i )
		{
			balls[i].pack(bitstream);
		}
	}
	
	void unpack( BitStream& bitstream )
	{
		unsigned int size;
		bitstream.Read( size );
		for ( int i = 0; i < size; ++i )
		{
			balls[i].unpack(bitstream);
		}
	}
	
	vector<Ball> balls;
};

class NetHandler
{
	public:
	
	NetHandler()
	{
		peer = RakNetworkFactory::GetRakPeerInterface();
	}
	
	virtual void update()
	{
		while ( Packet* packet = peer->Receive() )
		{
			if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED){
				connectionEvent(true);
			}
			else if (packet->data[0] == ID_CONNECTION_ATTEMPT_FAILED ){
				connectionEvent(false);
			}else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION ){
				incommingConnection();
			}else if (packet->data[0] == USER_PACKET ){
				BitStream b( packet->data+1, packet->length - 1, false );
				//cout << packet->systemAddress.ToString() << endl;
				userPacket(b);
			}
			peer->DeallocatePacket(packet);
		}
	}
	virtual void connectionEvent( bool a ) {}
	virtual void incommingConnection() {}
	virtual void userPacket( BitStream& data ) {}
	
	RakPeerInterface* peer;
};

int posx;

class Server : public NetHandler
{
	public:
	Server() : NetHandler()
	{
		peer->Startup(2, 10, &SocketDescriptor(14501,0), 1);
		peer->SetMaximumIncomingConnections(2);
	}
	
	virtual void update()
	{
		NetHandler::update();
		
		game.update();
		
		BitStream myBitStream;
		myBitStream.Write( (unsigned char)USER_PACKET );
		game.pack( myBitStream );
		peer->Send( &myBitStream, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true );
	}
	
	virtual void connectionEvent( bool a )
	{
		if ( a )
		{
			cout << "SERVER: CONNECTED" << endl;
		}else
		{
			cout << "SERVER: FAILED" << endl;
		}
	}
	
	virtual void incommingConnection()
	{
		cout << "SERVER: CLIENT CONNECTED" << endl;
	}
	
	virtual void userPacket( BitStream& bitstream )
	{
		//cout << "SERVER: USER PACKET RECIEVED" << endl;
		
		BitStream myBitStream;
		myBitStream.Write( (unsigned char)USER_PACKET );
		myBitStream.Write( (unsigned char) 14 );
		peer->Send( &myBitStream, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true );
	}
	
	Game game;
};

class Client : public NetHandler
{
	public:
	Client( const std::string& address ) : NetHandler()
	{
		peer->Startup(1, 10, &SocketDescriptor(), 1);
		if ( !peer->Connect( address.c_str(), 14501, 0, 0 ) )
			throw "shit";
	}
	
	virtual void update()
	{
		NetHandler::update();
		game.update();
	}
	
	virtual void connectionEvent( bool a )
	{
		if ( a )
		{
			cout << "CLIENT: CONNECTED" << endl;
			
			BitStream myBitStream;
			myBitStream.Write( (unsigned char)USER_PACKET );
			myBitStream.Write( (unsigned char) 14 );
			
			peer->Send( &myBitStream, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true );
		}else
		{
			cout << "CLIENT: CONNECTION FAILED" << endl;
		}
	}
	
	virtual void userPacket( BitStream& bitstream )
	{
		//cout << "CLIENT: PACKET RECIEVED" << endl;
		
		//bitstream.Read(posx);
		
		game.unpack( bitstream );
		
		/*BitStream myBitStream;
		myBitStream.Write( (unsigned char)USER_PACKET );
		myBitStream.Write( (unsigned char) 14 );
		peer->Send( &myBitStream, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true );*/
	}
	
	Game game;
};

int main()
{
	posx = 100;
	
	if (allegro_init() != 0)
		return 1;

    install_keyboard();

    set_color_depth(32);
    if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0) != 0) {
        if (set_gfx_mode(GFX_SAFE, 320, 200, 0, 0) != 0) {
            set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
            allegro_message("Unable to set any graphic mode\n%s\n", allegro_error);
        return 1;
        }
    }
    
    BITMAP* buffer = create_bitmap(680, 480);

    /*BITMAP *bmp;
    bmp = load_bitmap("../images/test.bmp", NULL);
    if (!bmp) {
        cout << ("NO PUDO CARGAR EL BMP");
    }else destroy_bitmap(bmp);
    SpriteMap spm(bmp);*/
    
    
    clear_bitmap(screen);
    
    /*for (int i = 0; i < 9; i++)
    {
        clear_bitmap(screen);
        spm.getSprite(i).render(screen,20,100);
        readkey();
    }*/
    //readkey();
    
	
	//Server
	Server* server = new Server();

	//Client
	Client* client = new Client( "localhost" );
	
	cout << endl;
	
	FPSLimiter fpsLimiter;
	
	while ( !key[KEY_ESC] )
	{
		poll_keyboard();
		
		server->update();
		client->update();
		
		if ( key[KEY_RIGHT] )
		{
			server->game.balls[0].xspd += 0.2;
		}else if ( key[KEY_LEFT] )
		{
			server->game.balls[0].xspd -= 0.2;
		}else if ( key[KEY_UP] )
		{
			server->game.balls[0].yspd -= 0.2;
		}else if ( key[KEY_DOWN] )
		{
			server->game.balls[0].yspd += 0.2;
		}
		
		
		
		clear_bitmap(buffer);
		client->game.render(buffer);
		
		blit(buffer, screen, 0, 0, 0, 0, buffer->w, buffer->h);
        //spm.getSprite(1).render(screen,posx,320);
		
		rest( 1000 / 50 );
		//vsync();
		//fpsLimiter.limitFps(50);
	}
	
	return 0;
}
END_OF_MAIN()
